import re

class Validator(object):
    #A dict of validation rules. Keys are the attributes to which the rules are
    #applied and values should be lists of validators that accept a name/value
    #pair.
    _rules = {}

    #List for holding errors.
    _errors = []
    
    #Messages is a dict of tuples. The keys correspond to validation methods and
    #the tuples are the error message and whether or not you want the attribute
    #name formatted in.
    _messages = {
        'default': {
            'not_empty': (
                'The %s field may not be empty',
                True
            ),
            'alpha': (
                'The %s field may contain only letters',
                True
            ),
            'alphanum': (
                'The %s field may only contain alphanumeric characters',
                True
            ),
            'email': (
                'Please enter a valid email address',
                False
            ),
            'no_spaces': (
                'The %s field may contain no spaces',
                True
            ),
            'name': (
                'Please enter valid name, it could not contain special characters',
                True
            ),
            'password': (
                'Please enter valid password, it should require at least one upper case one digit and one special character',
                True
            )
        }
    }

    regexes = {
        'email' : re.compile(r'(^$)|(^[A-Za-z0-9.%-]+@[A-Za-z0-9%-.]+.[a-zA-Z]{3}$)'),
        'password' : re.compile( r'.*(?=.{6,})(?=.*[a-zA-Z])(?=.*[\d])'), 
        'not_empty' : re.compile(r'.+'),
        'no_spaces' : re.compile(r'^\S+$'),
        'name' : re.compile(r'^[a-zA-Z]{1}[a-zA-Z0-9-_. ]{3,16}$'),
        'password' : re.compile(r'(?=^.{6,16}$)(?=.*\d)(?=.*\W+)(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$'),
    }

    def __setattr__(self, name, value):
        #Check if rules exist at all or at least for this attribute.
        if not self._rules:
            object.__setattr__(self, name, value)
        elif name not in self._rules:
            object.__setattr__(self, name, value)

        else:
            #First check for custom error messages.
            if name in self._messages:
                messages = self._messages[name]
            else:
                messages = self._messages['default']

            #Run the value through the rules.
            for rule in self._rules[name]:
                #If there are any issues it will throw an exception.
                try:
                    rule(name, value)
                    object.__setattr__(self, name, value)
                except:
                    message, pass_name = messages[rule.__name__]
                    if pass_name:
                        self._errors.append(message % name)
                    else:
                        self._errors.append(message)


    @staticmethod
    def not_empty(name, value):
        if not Validator.regexes['not_empty'].match(str(value)):
            raise Exception('Value is empty.')

    @staticmethod
    def alpha(name, value):
        if not value.isalpha():
            raise Exception('Value is non-alphabetic.')

    @staticmethod
    def alphanum(name, value):
        if not value.isalnum():
            raise Exception('Value is non-alphanumeric.')

    @staticmethod
    def email(name, value):
        if not Validator.regexes['email'].match(str(value)):
            raise Exception('Value is a non-valid email.')
        
    @staticmethod
    def name(name, value):
        if not Validator.regexes['name'].match(str(value)):
            raise Exception('Value is a non-valid name.')
        
    @staticmethod
    def password(name, value):
        if not Validator.regexes['password'].match(str(value)):
            raise Exception('Value is a non-valid password.')


    @staticmethod
    def no_spaces(name, value):
        if not Validator.regexes['no_spaces'].match(str(value)):
            raise Exception('Value contains spaces')

    def validate(self):
        if not self._errors:
            return True
        else:
            return False

    def errors(self):
        return self._errors

    def clear_errors(self):
        self._errors = []